<?php

declare(strict_types=1);

namespace App\Strategy;

class TrendFollowingContractStrategy
{
    protected $binance;

    private string $symbol;

    private $shortPeriod;

    private $longPeriod;

    private $rsiPeriod;

    private $overbought;

    private $oversold;

    private $maxSlippage; // 最大滑点容忍度
    private $leverage; // 杠杆倍数

    public function __construct($binance, $symbol, $shortPeriod = 50, $longPeriod = 200, $rsiPeriod = 14, $overbought = 70, $oversold = 30, $maxSlippage = 0.01, $leverage = 10)
    {
        $this->binance = $binance;
        $this->symbol = strtoupper($symbol);
        $this->shortPeriod = $shortPeriod;
        $this->longPeriod = $longPeriod;
        $this->rsiPeriod = $rsiPeriod;
        $this->overbought = $overbought;
        $this->oversold = $oversold;
        $this->maxSlippage = $maxSlippage;
        $this->leverage = $leverage;
    }

    public function getKlines(string $symbol, string $interval = '1m', int $limit = 100)
    {
        return $this->binance->getKlines($symbol, $interval, $limit);
    }

    public function calculateRSI(array $closes, int $period = 14)
    {
        $gains = 0;
        $losses = 0;

        for ($i = 1; $i < $period; ++$i) {
            $change = $closes[$i] - $closes[$i - 1];
            if ($change > 0) {
                $gains += $change;
            } else {
                $losses -= $change;
            }
        }

        $averageGain = $gains / $period;
        $averageLoss = $losses / $period;

        if ($averageLoss == 0) {
            return 100;
        }

        $rs = $averageGain / $averageLoss;
        return 100 - (100 / (1 + $rs));
    }

    /**
     * 判断是否需要买入或卖出合约.
     */
    public function handle(): array
    {
        // 获取 K 线数据
        $kLines = $this->getKlines($this->symbol, '1m', $this->longPeriod + $this->rsiPeriod);

        if (count($kLines) < $this->longPeriod) {
            return ['action' => 'none', 'reason' => '数据不足'];
        }

        // 提取收盘价
        $closes = array_column($kLines, 4);

        // 计算短期和长期均线
        $shortMa = array_sum(array_slice($closes, -$this->shortPeriod)) / $this->shortPeriod;
        $longMa = array_sum(array_slice($closes, -$this->longPeriod)) / $this->longPeriod;

        // 计算 RSI
        $rsi = $this->calculateRSI($closes, $this->rsiPeriod);

        // 获取市场的最佳买入和卖出价格
        $bestBidPrice = $this->getBestBidPrice();
        $bestAskPrice = $this->getBestAskPrice();

        // 判断买入信号：短期均线从下向上穿过长期均线，并且 RSI 没有进入超买区
        if ($shortMa > $longMa && $rsi < $this->overbought) {
            $currentPrice = end($closes);
            // 判断滑点，确保滑点在可接受范围内
            $slippage = abs(($currentPrice - $bestBidPrice) / $bestBidPrice);
            if ($slippage <= $this->maxSlippage) {
                // 进行合约买入操作
                return $this->placeContractOrder('buy', $currentPrice);
            }
            return ['action' => 'none', 'reason' => '滑点过大，跳过买入'];
        }

        // 判断卖出信号：短期均线从上向下穿过长期均线，或者 RSI 进入超买区
        if ($shortMa < $longMa || $rsi > $this->overbought) {
            $currentPrice = end($closes);

            // 判断滑点，确保滑点在可接受范围内
            $slippage = abs(($currentPrice - $bestAskPrice) / $bestAskPrice);
            if ($slippage <= $this->maxSlippage) {
                // 进行合约卖出操作
                return $this->placeContractOrder('sell', $currentPrice);
            }
            return ['action' => 'none', 'reason' => '滑点过大，跳过卖出'];
        }

        // 如果没有信号，返回不操作
        return ['action' => 'none'];
    }

    /**
     * 获取最佳买入价.
     */
    private function getBestBidPrice()
    {
        return $this->binance->getOrderBook($this->symbol)['bids'][0][0];
    }

    /**
     * 获取最佳卖出价.
     */
    private function getBestAskPrice()
    {
        return $this->binance->getOrderBook($this->symbol)['asks'][0][0];
    }

    /**
     * 下单操作，适用于合约交易.
     */
    private function placeContractOrder(string $side, float $price): array
    {
        // 这里可以调用合约交易的下单接口
        // 示例：返回下单结果
        $quantity = 1; // 示例数量，实际应用中要根据账户余额和杠杆计算

        return $this->binance->createContractOrder($this->symbol, $side, $quantity, $price, $this->leverage);
    }
}